#include <string>
#include <vector>
#include "StatefulUnit.h"
#include "Level.h"
#include "Physics.h"

StatefulUnit::StatefulUnit() : BaseUnit()
{
    init();
}

StatefulUnit::StatefulUnit(const Vector2df p, const Vector2df v) : BaseUnit(p,v)
{
    init();
}

StatefulUnit::StatefulUnit(string spriteFilename, const Vector2df p, const Vector2df v, CRstring parameter) : BaseUnit(p,v)
{
    init();
    loadSprite(spriteFilename);
    setSpriteState("Default");
    m_isGeneric = true;
}

StatefulUnit::StatefulUnit(string spriteFilename) : BaseUnit()
{
    init();
    m_statefulSprite = new StatefulSprite(spriteFilename);
}

StatefulUnit::~StatefulUnit()
{
    m_sprite = NULL;
    m_collisionRegion = NULL;;
    delete m_statefulSprite;
    //dtor
}

void StatefulUnit::init()
{
    m_sprite = NULL;
    m_collisionRegion = NULL;
    m_statefulSprite = NULL;
    m_isGeneric = false;
    lastFrame = -1;
}

int StatefulUnit::getWidth() const
{
    if (m_collisionRegion)
    {
        return m_collisionRegion->getWidth();
    }
    else if (m_sprite)
    {
        return m_sprite->getWidth();
    }
    else
    {
        return m_size.x;
    }
}

int StatefulUnit::getHeight() const
{
    if (m_collisionRegion)
    {
        return m_collisionRegion->getHeight();
    }
    else if (m_sprite)
    {
        return m_sprite->getHeight();
    }
    else
    {
        return m_size.y;
    }
}

Vector2df StatefulUnit::getSize() const
{
    if (m_collisionRegion)
    {
        return Vector2df(m_collisionRegion->getWidth(),m_collisionRegion->getHeight());
    }
    else if (m_sprite)
    {
        return Vector2df(m_sprite->getWidth(),m_sprite->getHeight());
    }
    else
    {
        return m_size;
    }
}

Vector2df StatefulUnit::getPositionOffset(CRbool inTiles) const
{
    Vector2df Result(0,0);
    if (m_collisionRegion)
    {
        Result = m_collisionRegion->getRegionOffset();
        if (inTiles)
            Result /= (float)getLevel()->GetTileSize();
    }
    return Result;
}


void StatefulUnit::loadSprite(string spriteFilename)
{
    m_statefulSprite = new StatefulSprite(spriteFilename);
}

// Set the current state
void StatefulUnit::setSpriteState(string stateName)
{
    if (m_statefulSprite->getSpriteState() != stateName)
    {
        m_statefulSprite->setSpriteState(stateName);
        m_sprite = m_statefulSprite->getSpriteForState(stateName);
        m_collisionRegion = m_statefulSprite->getCollisionForState(stateName);
    }
}

void StatefulUnit::update()
{
    m_counter.update();

    if (m_sprite) m_sprite->update();

    // set position above hero if being carried
    if (isBeingCarried())
    {
        // TODO: This code is split on BaseUnit (setParent function, first call) and here
        // I think it probably should go in the HeroClass implementation somehow, as child unit classes might override update() and would need to keep this at any cost
        if (m_parent->getDirection() == 1)
            setPosition(Vector2df(m_parent->getPosition().x + 24,m_parent->getPosition().y - getHeight() - 8) + m_parent->m_velocity - getPositionOffset());
        else
            setPosition(Vector2df(m_parent->getPosition().x  - 14,m_parent->getPosition().y - getHeight() - 8) + m_parent->m_velocity - getPositionOffset());
    }

    // frame events
    if (m_statefulSprite && m_sprite && lastFrame != m_sprite->getCurrentFrame())
    {
        lastFrame = m_sprite->getCurrentFrame();

        m_statefulSprite->playFrameSound(m_sprite->getCurrentFrame());

        vector< vector< string > > events = m_statefulSprite->getFrameEvents(m_statefulSprite->getSpriteState(),m_sprite->getCurrentFrame());
        if (events.size() > 0)
        {
            vector< vector<string> >::iterator it;
            for (it=events.begin(); it!=events.end(); ++it)
            {
                processEvent((*it));
            }
        }
    }

    // physics
    if (getCollision() && not isBeingCarried())
        PHYSICS->calculatePhysics(this);
};

void StatefulUnit::checkCollision(vector< BaseUnit* >* const enemies, StatefulUnit* hero)
{
    if (getCollision() && not hasCollisionFlag(CollisionFlags::NO_UNITS))
    {
        if (enemies)
            PHYSICS->checkUnitCollision(this,enemies);
        if (hero)
            PHYSICS->checkHeroCollision(this,hero);
    }

    if (getCollision())
        PHYSICS->checkMapCollision(this);
}

void StatefulUnit::hit(const SimpleDirection dir, BaseUnit* const player, CRstring parameter)
{
    handleEvent("Hit");
}

void StatefulUnit::hitMap(MapData* const data, const CollisionObject* const obj)
{
    Vector2df correction = obj->getTotalCorrection();

    if (correction != Vector2df(0,0))
        handleEvent("HitMap");

    incY(m_velocity.y + correction.y);
    if (abs(correction.y) > 0)
    {
        m_velocity.y = 0;
        accelerateY(0,0);
    }
    if (correction.x != 0)
    {
        // Change movement direction
        m_velocity.x *= -1;
        m_acceleration[0].x *= -1;
        m_acceleration[1].x *= -1;
    }
    incX(m_velocity.x + correction.x);
}

void StatefulUnit::handleEvent(string eventName)
{
    if (m_statefulSprite)
    {
        vector< vector< string > > events = m_statefulSprite->getEvents(eventName);
        if (events.size() > 0)
        {
            vector< vector<string> >::iterator it;
            for (it=events.begin(); it!=events.end(); ++it)
            {
                processEvent((*it));
            }
        }
    }
}

bool StatefulUnit::processEvent(vector< string > event)
{
    if (event.size() > 1)
    {
        if (event[0] == "Hero")
        {
            return false;
        }
        else if (event[0] == "Level")
        {
            return false;
        }
        else if (event[0] == "Self")
        {
            if (event[1] == "Remove")
            {
                m_shouldBeRemoved = true;
                return true;
            }
            else if (event[1] == "Sound")
            {
                if (event.size()>2)
                    playSound(event[2]);
                return true;
            }
            else if (event[1] == "State")
            {
                if (event.size()>2)
                {
                    setSpriteState(event[2]);
                    if (event.size()>3)
                        m_sprite->setCurrentFrame(atoi(event[3].c_str()));
                }
                return true;
            }
            else if (event[1] == "Speed")
            {
                if (event.size() < 5)
                {
                    for (int I = 2; I < event.size(); ++I)
                    {
                        vector<string> split;
                        StringUtility::tokenize(event[I],split,"=",2);
                        if (split[0] == "x" || split[0] == "X")
                            m_velocity.x = atoi(split[1].c_str());
                        else if (split[0] == "y" || split[0] == "Y")
                            m_velocity.y = atoi(split[1].c_str());
                        else if (split[0] == "x+" || split[0] == "X+")
                            m_velocity.x += atoi(split[1].c_str());
                        else if (split[0] == "y+" || split[0] == "Y+")
                            m_velocity.y += atoi(split[1].c_str());
                        else if (split[0] == "x-" || split[0] == "X-")
                            m_velocity.x -= atoi(split[1].c_str());
                        else if (split[0] == "y-" || split[0] == "Y-")
                            m_velocity.y -= atoi(split[1].c_str());
                    }
                }
                return true;
            }
        }
    }
    return false;
}

void StatefulUnit::releaseFromBeingCarried(const SimpleDirection& dir)
{
    switch (dir.value)
    {
    case diLEFT:
        m_velocity.y = -6;
        m_velocity.x = 6;
        break;
    case diRIGHT:
        m_velocity.y = -6;
        m_velocity.x = -6;
        break;
    case diTOP:
        m_velocity.y = 6;
        m_velocity.x = 3;
        break;
    case diBOTTOM:
        m_velocity.y = -6;
        m_velocity.x = 3;
        break;
    }
}

void StatefulUnit::render()
{
    BaseUnit::render();
#ifdef _DEBUG
    if (m_collisionRegion)
    {
        m_collisionRegion->setPosition(getScreenPosition());
        m_collisionRegion->render();
        m_collisionRegion->setPosition(Vector2df(0,0));
    }
#endif
};
